/**
 * @file Driver_Flash.c
 * @brief 
 * @author 2bam 
 * @version 1.0
 * @date 2024-11-24
 * @copyright Copyright (c) 2024  NULL
 * 
 * @change log:
 * Date           Version Author   Describe
 * 2024-11-24     1.0     2bam     first version
 */


/* Includes -------------------------------------------------------------------*/
#include "main.h"
#include "Driver_Flash.h"
#include <stdio.h>
#include "config.h"
/* Private define -------------------------------------------------------------*/
#define CPU_SIZE_ADDR   0x1FFFF7E0

#define CPU_UID_ADDR1	(0x1FFFF7E8)
#define CPU_UID_ADDR2   (CPU_UID_ADDR1+0x02)
#define CPU_UID_ADDR3	(CPU_UID_ADDR1+0x04)
#define	CPU_UID_ADDR4	(CPU_UID_ADDR1+0x08)
/* Private typedef ------------------------------------------------------------*/

/* Private variables ----------------------------------------------------------*/

/* Private function prototypes ------------------------------------------------*/




/**
 * @brief 读取字
 * @param  addr        My Param doc
 * @return uint32_t 
 * @date 2024-11-24
 * @author 2bam 
 */
uint32_t Flash_ReadWord(uint32_t addr)
{
	return (*(uint32_t *)(addr));
}

/**
 * @brief 读取半字
 * @param  addr        My Param doc
 * @return uint16_t 
 * @date 2024-11-24
 * @author 2bam 
 */
uint16_t Flash_ReadHalfWord(uint32_t addr)
{
	return (*(uint16_t *)(addr));
}

/**
 * @brief 读取字节
 * @param  addr        My Param doc
 * @return uint8_t 
 * @date 2024-11-24
 * @author 2bam 
 */
uint8_t Flash_ReadByte(uint32_t addr)
{
	return (*(uint8_t *)(addr));
}

/**
 * @brief 页擦除
 * @param  addr        My Param doc
 * @date 2024-11-24
 * @author 2bam 
 */
void Flash_ErasePages(uint32_t addr)
{
	//extern void FLASH_PageErase(uint32_t PageAddress);
	/** HAL库中的FLASH_PageErase函数中存在BUG，它没有在擦除后清除标志位，导致
	 * 无法对页进行编程操作。
	 */
	HAL_FLASH_Unlock();
	/* Proceed to erase the page */
    SET_BIT(FLASH->CR, FLASH_CR_PER);
    WRITE_REG(FLASH->AR, addr);
    SET_BIT(FLASH->CR, FLASH_CR_STRT);
	/** 一定要等待空闲 */
	while(READ_BIT(FLASH->SR,FLASH_SR_BSY));
	/** 需要补充清除标志位的操作 2024-11-24 Sunday 09:19:26 */
	CLEAR_BIT(FLASH->CR, (FLASH_CR_PER));
	HAL_FLASH_Lock();
}


/**
 * @brief  字编程
 * @param  addr        My Param doc
 * @param  data        My Param doc
 * @date 2024-11-24
 * @author 2bam 
 */
void Flash_ProgramWord(uint32_t addr, uint32_t data)
{
	HAL_FLASH_Unlock();
	HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,FLASH_SECTOR_255_ADDR,data);
	HAL_FLASH_Lock();
}


/**
 * @brief Get the CpuInfo object
 * @date 2024-11-24
 * @author 2bam 
 */
void GetCpuInfo(void)
{
    printf("-----CPU INFO-----\n");
	printf("FlashSize:%#04x\n",Flash_ReadHalfWord(CPU_SIZE_ADDR));
	printf("UID:%04x\n",Flash_ReadHalfWord(CPU_UID_ADDR1));
	printf("UID:%04x\n",Flash_ReadHalfWord(CPU_UID_ADDR2));
	printf("UID:%08lx\n",Flash_ReadWord(CPU_UID_ADDR3));
	printf("UID:%08lx\n",Flash_ReadWord(CPU_UID_ADDR4));
	printf("SystemFreq:%ldHz\n",SystemCoreClock);
}



/**
 * @brief 以下3个函数为正点原子对页擦除的操作，亲测可用
 * @return uint8_t 
 * @date 2024-11-24
 * @author 2bam 
 */
static uint8_t stmflash_get_error_status(void)
{
    uint32_t res;
    res = FLASH->SR;
 
    if (res & (1 << 0))return 1;    /* BSY = 1      , 忙 */
    if (res & (1 << 2))return 2;    /* PGERR = 1    , 编程错误*/
    if (res & (1 << 4))return 3;    /* WRPRTERR = 1 , 写保护错误 */
    
    return 0;   /* 没有任何错误 操作完成. */
}

/**
 * @brief 
 * @param  time        My Param doc
 * @return uint8_t 
 * @date 2024-11-24
 * @author 2bam 
 */
static uint8_t stmflash_wait_done(uint32_t time)
{
    uint8_t res;
 
    do
    {
        res = stmflash_get_error_status();
 
        if (res != 1)
        {
            break;      /* 非忙, 无需等待了, 直接退出 */
        }
        
        time--;
    } while (time);
 
    if (time == 0)res = 0XFF;   /* 超时 */
 
    return res;
}
 
/**
 * @brief       擦除扇区
 * @param       saddr   : 扇区地址 0 ~ 256
 * @retval      执行结果
 *   @arg       0   : 已完成
 *   @arg       2   : 编程错误
 *   @arg       3   : 写保护错误
 *   @arg       0XFF: 超时
 */
uint8_t stmflash_erase_sector(uint32_t saddr)
{
    uint8_t res = 0;  /* STM32F103擦除的时候是指定半字地址 */
    res = stmflash_wait_done(0X5FFFFF);     /* 等待上次操作结束, >20ms */
 
    if (res == 0)
    {
        FLASH->CR |= 1 << 1;    /* 页擦除 */
        FLASH->AR = saddr;      /* 设置页地址(实际是半字地址) */
        FLASH->CR |= 1 << 6;    /* 开始擦除 */
        res = stmflash_wait_done(0X5FFFFF); /* 等待操作结束, >20ms */
 
        if (res != 1)   /* 非忙 */
        {
            FLASH->CR &= ~(1 << 1); /* 清除页擦除标志 */
        }
    }
 
    return res;
}


